using System;
using System.IO;
using System.Xml;
using System.Collections.Generic;

namespace tvgrabau
{
	public static class Generator
	{
		private static long ProgrammeCount = 0;
		public static int x = 0;
		private static bool GettingTodayOnly = false;
		private static double[] Progress = new double[4];
	    
	    /// <summary>
	    /// Fetches and processes programmes for the day specified.
	    /// </summary>
		public static List<Programme>[] ProcessDay(DateTime dt)
		{
			// Initialize Progress[4] with 0s..
			for (int q = 0; q < 4; q++)
			{
				Progress[q] = 0;
			}
		
			List<Programme>[] PListArray = new List<Programme>[4];
			
			if (dt.ToString("yyyy-MM-dd") == DateTime.Now.ToString("yyyy-MM-dd"))
			{
				GettingTodayOnly = true;
				// The day to process is actually today - save bandwidth and use GetRestDay();
				PListArray = new List<Programme>[1];
				string[] PIDs = Generator.FindPIDs(WebActions.GetRestDay());
				PListArray[0] = GatherDetails(PIDs);
			}
			else
			{
				// We're processing a day *other* than today, so fetch it normally..
				while (x < 4)
				{
					GettingTodayOnly = false;
					string[] PIDs = Generator.FindPIDs(WebActions.GetPeriod((x + 1), dt.ToString("yyyy-MM-dd")));
					PListArray[x] = GatherDetails(PIDs);
					x++;
				}
			}
									
			x = 0;
			return PListArray;
		}
		
		/// <summary>
		/// Creates a string array containing the programme IDs found in the HTML page.
		/// </summary>
		public static string[] FindPIDs(string lcHtml)
		{
			// TODO: Enable FindPIDs support via mirror..
			return FindPIDs_YourTV(lcHtml);
		}
		
		/// <summary>
		/// YourTV specific version of FindPIDs().
		/// </summary>
		public static string[] FindPIDs_YourTV(string lcHtml)
		{
			// Ported from original version, cleaned up a bit..
		
		    long programcount = 0;
        	char javascriptSplitCharacter = ',';
            bool moretofind = true;
            string PIDlist = "";
            int offset = 0;
            
        	if (lcHtml.Length > 10)
        	{				while (moretofind)
            	{
                	int sidx = lcHtml.IndexOf("session_info(", offset);
                	if (sidx == -1)
                	{
	                    moretofind = false;
                	}
                	else
                	{
                        int eidx = YourTV.findNextChar(javascriptSplitCharacter, lcHtml, sidx);
                        sidx += 13;
                        PIDlist += lcHtml.Substring(sidx, eidx - sidx) + ",";
                        offset = sidx + 1;
                        programcount++;
                    }
                }
            }
            
            // Split string into string array.
            PIDlist = PIDlist.Replace("'", "");
            if (PIDlist.Length > 1)
            {
            	PIDlist = PIDlist.Remove(PIDlist.Length - 1, 1);
            }
            else
            {
                if (MainClass.loud)
                {
            	    Console.WriteLine();
            	    Console.WriteLine("[ERROR]: Failed to find any programmes via YourTV. This is most likely because their browser-checking mechanism and/or their site design has changed.");
            	    Console.WriteLine("Cannot continue.");
            	}
            	Environment.Exit(3);
            }
            char[] splitCharacter = { ',' };
            ProgrammeCount += programcount;
            return PIDlist.Split(splitCharacter);
		}
		
		/// <summary>
		/// Creates a Programme list based on the PIDs.
		/// </summary>
		public static List<Programme> GatherDetails(string[] PIDs)
		{
			List<Programme> PList = new List<Programme>();
			bool silent = false;
			
			double p = 0;
			double done = 0;
			double total = (double)PIDs.Length;
			
			foreach(string PID in PIDs)
			{
				bool failed = false;
				Programme Insert = new Programme();
				try
				{
					Insert = GatherDetails_File(PID);
				}
				catch (System.IO.FileNotFoundException)
				{
					failed = true;
				}
				catch (System.IO.DirectoryNotFoundException)
				{
					if (MainClass.loud && silent == false)
					{
						Console.WriteLine("[WARNING]: Cache directory not found!");
					}
					silent = true;
					failed = true;
				}
				finally
				{
					if (failed)
					{
						Insert = GatherDetails_Mirror(PID);
						
						if (Insert.Type == ProgrammeType.Errored)
						{
							Insert = GatherDetails_YourTV(PID);
						}
					}
				}
				
				p = (done / total) * 100;
				if (!GettingTodayOnly)
				{
					Progress[x] = p;
					double tp = 0;
					
					foreach (double d in Progress)
					{
						tp += d;
					}
					YourTV.Percent = tp / 4;
				}
				else
				{
					YourTV.Percent = p;
				}
				YourTV.DrawProgress();
				done++;
				
				// Setup ID!
				Insert.ID = int.Parse(PID);
				
				PList.Add(Insert);
			}
			
			return PList;
		}
		
		/// <summary>
		/// YourTV specific helper function for GetDetails().
		/// </summary>
		public static Programme GatherDetails_YourTV(string PID)
		{
			Programme t = YTVParser.GatherDetails(WebActions.GetProgramme(PID, true));
			if (t.Type != ProgrammeType.Errored && !MainClass.options.NoDescriptions)
			{
			    // Insert the programme into the public mirror's DB!
				WebActions.InsertProgramme(PID, t);
			}
			
			return t;
		}
		
		/// <summary>
		/// Public mirror helper function for GetDetails().
		/// </summary>
		public static Programme GatherDetails_Mirror(string PID)
		{
			string i = WebActions.GetProgramme(PID, false);
			Programme p = ParseMirror(i);
			if (p.Type != ProgrammeType.Errored)
			{
			    // Write the programme to disk (file cache).
				FileStream file = new FileStream(MainClass.options.CacheLocation + PID, FileMode.Create, FileAccess.Write);
				StreamWriter sw = new StreamWriter(file);
				
				sw.Write(i);
			
				sw.Close();
				file.Close();
			}
			
			return p;
		}
		
		/// <summary>
		/// File-based helper function for GetDetails().
		/// </summary>
		public static Programme GatherDetails_File(string PID)
		{
			FileStream file = new FileStream(MainClass.options.CacheLocation + PID, FileMode.Open, FileAccess.Read);
			StreamReader sr = new StreamReader(file);
			
			string s = sr.ReadToEnd();
			
			sr.Close();
			file.Close();
			
			return ParseMirror(s);
		}
		
		/// <summary>
		/// Parses XML from public mirror or file, and converts it into a Programme.
		/// </summary>
		public static Programme ParseMirror(string data)
		{
			Programme p = new Programme();
			
			XmlDocument d = new XmlDocument();
			d.LoadXml(data);
			
			XmlNode rootNode = d.DocumentElement;
			XmlNodeList children = rootNode.ChildNodes;
			
			foreach (XmlNode n in children)
			{
				if (n.InnerText != "")
				{
					if (n.LocalName == "title")
					{
						p.Title = n.InnerXml;
					}
					if (n.LocalName == "episode")
					{
						p.EpisodeName = n.InnerXml;
					}
					if (n.LocalName == "description")
					{
						p.Description = n.InnerXml;
					}
					if (n.LocalName == "start")
					{
						p.TimeStart = DateTime.SpecifyKind(DateTime.Parse(n.InnerText), DateTimeKind.Utc).ToLocalTime();
					}
					if (n.LocalName == "stop")
					{
						p.TimeEnd = DateTime.SpecifyKind(DateTime.Parse(n.InnerText), DateTimeKind.Utc).ToLocalTime();
					}
					if (n.LocalName == "category")
					{
						p.Category = n.InnerXml;
					}
					if (n.LocalName == "widescreen")
					{
						p.Widescreen = true;
					}
					if (n.LocalName == "repeat")
					{
						p.Repeat = true;
					}
					if (n.LocalName == "teletext")
					{
						p.Teletext = true;
					}
					if (n.LocalName == "channel")
					{
						p.ChannelName = n.InnerText;	// Don't use InnerXml here!
					}
					if (n.LocalName == "type")
					{
						if (n.InnerText == "0")
						{
							p.Type = ProgrammeType.Normal;
						}
						else if (n.InnerText == "1")
						{
							p.Type = ProgrammeType.Movie;
						}
						else if (n.InnerText == "2")
						{
							p.Type = ProgrammeType.Sports;
						}
						else if (n.InnerText == "3")
						{
							p.Type = ProgrammeType.Series;
						}
					}
					if (n.LocalName == "premiere")
					{
						p.Premiere = true;
					}
					if (n.LocalName == "country")
					{
						p.Country = n.InnerText;
					}
					if (n.LocalName == "classification")
					{
						p.Classification = n.InnerText;
					}
					if (n.LocalName == "hdtv")
					{
						p.HDTV = true;
					}
					if (n.LocalName == "final")
					{
						p.Final = true;
					}
					if (n.LocalName == "year")
					{
						p.Year = n.InnerText;
					}
					if (n.LocalName == "cast")
					{
						p.Cast = n.InnerXml;
					}
					if (n.LocalName == "score" && n.InnerText != "0")
					{
						p.Score = n.InnerText;
					}
					
					// If we got an error, report this programmed as errored (duh).
					if (n.LocalName == "error")
					{
						p.Type = ProgrammeType.Errored;
						// Check to see if it was only a not-found error (in which case, only mark now and fetch from YourTV later).
						if (n.InnerText != "Programme not found." && MainClass.loud)
						{
							Console.WriteLine();
							Console.WriteLine("[WARNING]: Got error from mirror during fetch: {0}", n.InnerText);
						}
					}
				}
			}
			
			return p;
		}
	}
}
